package com.booter.ws.config;

import com.booter.ws.handler.RouteWebSocketInterceptor;
import com.booter.ws.handler.WebSocketChannelInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.ChannelRegistration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketTransportRegistration;

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    private static long HEART_BEAT = 5000;

    @Autowired
    private WebSocketChannelInterceptor channelInterceptor;

    @Autowired
    private RouteWebSocketInterceptor webSocketInterceptor;

    @Bean(destroyMethod = "shutdown")
    public ThreadPoolTaskScheduler socketHandlerScheduler() {
        ThreadPoolTaskScheduler te = new ThreadPoolTaskScheduler();
        te.setPoolSize(20);
        te.setThreadNamePrefix("wss-handler-thread-");
        te.initialize();
        return te;
    }

    @Bean(destroyMethod = "shutdown")
    public ThreadPoolTaskScheduler outBoundScheduler() {
        ThreadPoolTaskScheduler te = new ThreadPoolTaskScheduler();
        te.setPoolSize(20);
        te.setThreadNamePrefix("wss-heartbeat-thread-");
        te.initialize();
        return te;
    }


    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/ws").addInterceptors(webSocketInterceptor)
                .setAllowedOriginPatterns("*")
                //.setAllowedOrigins("*") //低版本的spring boot使用此配置
                .withSockJS().setDisconnectDelay(3_000);
    }


    /**
     * @param registry
     * @MessageMapping、@SubscribeMapping、@SendTo、@SendToUser 等注解
     * registry.enableSimpleBroker(" / queue ", " / topic ");
     * registry.setApplicationDestinationPrefixes("/app");//注意此处有设置
     * registry.setUserDestinationPrefix("/user");
     */
    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        //客户端订阅地址的前缀信息
        registry.enableSimpleBroker("/topic", "/user");
        //用户推送消息至服务端接收地址的前缀
        //设置我们前端发送：websocket请求的前缀地址。即client.send("/ws-send")作为前缀，然后再加上对应的@MessageMapping后面的地址
        registry.setApplicationDestinationPrefixes("/topic");
        //用户订阅自己的消息，服务端推送消息 1对1
        registry.setUserDestinationPrefix("/user");
        //其中/user和/topic都是我们发送到前台的数据前缀。前端必须订阅以/user开始的消息（.subscribe()进行监听）。
        //setHeartbeatValue设置后台向前台发送的心跳，
        //注意：setHeartbeatValue这个不能单独设置，不然不起作用，要配合后面setTaskScheduler才可以生效。
        //对应的解决方法的网址：https://stackoverflow.com/questions/39220647/spring-stomp-over-websockets-not-scheduling-heartbeats
        registry.enableSimpleBroker("/user", "/topic").setHeartbeatValue(new long[]{HEART_BEAT, HEART_BEAT}).setTaskScheduler(outBoundScheduler());
    }

    /**
     * 指定消息字节大小，缓存大小，发送超时时间
     *
     * @param registration
     */
    @Override
    public void configureWebSocketTransport(WebSocketTransportRegistration registration) {
        /*
         * 1. setMessageSizeLimit 设置消息缓存的字节数大小 字节
         * 2. setSendBufferSizeLimit 设置websocket会话时，缓存的大小 字节
         * 3. setSendTimeLimit 设置消息发送会话超时时间，毫秒
         */
        registration.setMessageSizeLimit(10240)
                .setSendBufferSizeLimit(10240)
                .setSendTimeLimit(5000);
    }

    @Override
    public void configureClientOutboundChannel(ChannelRegistration registration) {
        /*
         * 配置消息线程池
         * 1. corePoolSize 配置核心线程池，当线程数小于此配置时，不管线程中有无空闲的线程，都会产生新线程处理任务
         * 2. maxPoolSize 配置线程池最大数，当线程池数等于此配置时，不会产生新线程
         * 3. keepAliveSeconds 线程池维护线程所允许的空闲时间，单位秒
         */
        registration.taskExecutor().corePoolSize(5)
                .maxPoolSize(20)
                .keepAliveSeconds(60);

    }

    @Override
    public void configureClientInboundChannel(ChannelRegistration registration) {
        registration.interceptors(channelInterceptor);
    }
}